#include "CpuZ80.h"
#include "CpuZ80Macros.h"
#include <cassert>

byte_t CpuZ80::HandleOpcode_DD(byte_t opcode)
{
	switch (opcode)
	{
	
		// Forward to double prefixed handler
		case 0xCB: 
		{
			// First the operand, then the opcode
			byte_t operand = FetchByte();
			byte_t opcode = FetchByte();
			return HandleOpcode_DDCB(operand, opcode);
		}																		break;

		
			
		// Handle opcodes

#pragma region Index Add

		case 0x09: RegIX = AddWord(RegIX, RegBC);								break; // ADD IX, BC
		case 0x19: RegIX = AddWord(RegIX, RegDE);								break; // ADD IX, DE
		case 0x29: RegIX = AddWord(RegIX, RegIX);								break; // ADD IX, IX
		case 0x39: RegIX = AddWord(RegIX, RegSP);								break; // ADD IX, SP

#pragma endregion

		case 0x34:
		{
			word_t address = AddDisplacement(RegIX, FetchByte());
			WriteByte(IncrementByte(ReadByte(address)), address);
		}																		break;	// INC (IX+N)
		case 0x35:
		{
			word_t address = AddDisplacement(RegIX, FetchByte());
			WriteByte(DecrementByte(ReadByte(address)), address);
		}																		break;	// DEC (IX+N)
		case 0x36:
		{
			word_t address = AddDisplacement(RegIX, FetchByte());
			WriteByte(FetchByte(), address);
		}																		break; 	// LD (IX+N),N

#pragma region Index Increment and Decrement

		case 0x23: RegIX++;														break; // INC IX
		case 0x2B: RegIX--;														break; // DEC IX

		case 0x24: RegIXH = IncrementByte(RegIXH);								break; // INC IXH
		case 0x25: RegIXH = DecrementByte(RegIXH);								break; // DEC IXH

		case 0x2C: RegIXL = IncrementByte(RegIXL);								break; // INC IXL
		case 0x2D: RegIXL = DecrementByte(RegIXL);								break; // DEC IXL

#pragma endregion

#pragma region Index Load and Store

		case 0x21: RegIX = FetchWord();											break; // LD IX, NN
		case 0x2A: RegIX = ReadWord(FetchWord());								break; // LD IX,(NN) 
		case 0x22: WriteWord(RegIX, FetchWord());								break; // LD (NN),IX

		case 0x46: RegB = ReadByte(AddDisplacement(RegIX, FetchByte()));		break; // LD B, (IX+d)
		case 0x4E: RegC = ReadByte(AddDisplacement(RegIX, FetchByte()));		break; // LD C, (IX+d)
		case 0x56: RegD = ReadByte(AddDisplacement(RegIX, FetchByte()));		break; // LD D, (IX+d)
		case 0x5E: RegE = ReadByte(AddDisplacement(RegIX, FetchByte()));		break; // LD E, (IX+d)
		case 0x66: RegH = ReadByte(AddDisplacement(RegIX, FetchByte()));		break; // LD H, (IX+d)
		case 0x6E: RegL = ReadByte(AddDisplacement(RegIX, FetchByte()));		break; // LD L, (IX+d)
		case 0x7E: RegA = ReadByte(AddDisplacement(RegIX, FetchByte()));		break; // LD A, (IX+d)

		case 0x70: WriteByte(RegB, AddDisplacement(RegIX, FetchByte()));		break; // LD (IX+d), B
		case 0x71: WriteByte(RegC, AddDisplacement(RegIX, FetchByte()));		break; // LD (IX+d), C
		case 0x72: WriteByte(RegD, AddDisplacement(RegIX, FetchByte()));		break; // LD (IX+d), D
		case 0x73: WriteByte(RegE, AddDisplacement(RegIX, FetchByte()));		break; // LD (IX+d), E
		case 0x74: WriteByte(RegH, AddDisplacement(RegIX, FetchByte()));		break; // LD (IX+d), H
		case 0x75: WriteByte(RegL, AddDisplacement(RegIX, FetchByte()));		break; // LD (IX+d), L
		case 0x77: WriteByte(RegA, AddDisplacement(RegIX, FetchByte()));		break; // LD (IX+d), A

#pragma endregion

		case 0x86: AddByte(ReadByte(AddDisplacement(RegIX, FetchByte())));			break;	// ADD A,(IX+N)
		case 0x8E: AddByte(ReadByte(AddDisplacement(RegIX, FetchByte())), true);	break;	// ADC A,(IX+N) 
		case 0x96: SubByte(ReadByte(AddDisplacement(RegIX, FetchByte())));			break;	// SUB (IX+N)
		case 0x9E: SubByte(ReadByte(AddDisplacement(RegIX, FetchByte())), true);	break;	// SBC (IX+N)
		case 0xA6: AndByte(ReadByte(AddDisplacement(RegIX, FetchByte())));			break;	// AND (IX+N)
		case 0xAE: XorByte(ReadByte(AddDisplacement(RegIX, FetchByte())));			break;	// XOR (IX+N)
		case 0xB6: OrByte(ReadByte(AddDisplacement(RegIX, FetchByte())));			break;	// OR (IX+N)
		case 0xBE: CompareByte(ReadByte(AddDisplacement(RegIX, FetchByte())));		break;	// CP (IX+N)		
		
		case 0xE1: RegIX = PopWord();												break;	// POP IX

		case 0xE3: 		
		{
			word_t data = ReadWord(RegSP);
			WriteWord(RegIX, RegSP);
			RegIX = data;
		}																			break;	// EX (SP), IX

		case 0xE5: PushWord(RegIX);													break;	// PUSH IX

		case 0xE9: JumpOnCondition(RegIX, true);									break;	// JP (IX)

		case 0xF9: RegSP = RegIX;													break;	// LD SP, IX


#pragma region Undocumented 

		case 0x26: RegIXH = FetchByte();											break; // LD HX,nn
		case 0x2E: RegIXL = FetchByte();											break; // LD LX,nn

		case 0x44: RegB = RegIXH;													break; // LD B,HX
		case 0x45: RegB = RegIXL;													break; // LD B,LX
		case 0x4C: RegC = RegIXH;													break; // LD C,HX
		case 0x4D: RegC = RegIXL;													break; // LD C,LX
		case 0x54: RegD = RegIXH;													break; // LD D,HX
		case 0x55: RegD = RegIXL;													break; // LD D,LX
		case 0x5C: RegE = RegIXH;													break; // LD E,HX
		case 0x5D: RegE = RegIXL;													break; // LD E,LX

		case 0x60: RegIXH = RegB;													break; // LD HX,B
		case 0x61: RegIXH = RegC;													break; // LD HX,C
		case 0x62: RegIXH = RegD;													break; // LD HX,D
		case 0x63: RegIXH = RegE;													break; // LD HX,E
		case 0x64: RegIXH = RegIXH;													break; // LD HX,HX
		case 0x65: RegIXH = RegIXL;													break; // LD HX,LX
		case 0x67: RegIXH = RegA;													break; // LD HX,A

		case 0x68: RegIXL = RegB;													break; // LD LX,B
		case 0x69: RegIXL = RegC;													break; // LD LX,C
		case 0x6A: RegIXL = RegD;													break; // LD LX,D
		case 0x6B: RegIXL = RegE;													break; // LD LX,E
		case 0x6C: RegIXL = RegIXH;													break; // LD LX,HX
		case 0x6D: RegIXL = RegIXL;													break; // LD LX,LX
		case 0x6F: RegIXL = RegA;													break; // LD LX,A

		case 0x7C: RegA = RegIXH;													break; // LD A,HX 	
		case 0x7D: RegA = RegIXL;													break; // LD A,LX 
	
		case 0x84: AddByte(RegIXH);													break; // ADD A, IXH
		case 0x85: AddByte(RegIXL);													break; // ADD A, IXL
		case 0x8C: AddByte(RegIXH, true);											break; // ADC A, IXH
		case 0x8D: AddByte(RegIXL, true);											break; // ADC A, IXL

		case 0x94: SubByte(RegIXH);													break; // SUB IXH
		case 0x95: SubByte(RegIXL);													break; // SUB IXL
		case 0x9C: SubByte(RegIXH, true);											break; // SBC IXH
		case 0x9D: SubByte(RegIXL, true);											break; // SBC IXL

		case 0xA4: AndByte(RegIXH);													break; 
		case 0xA5: AndByte(RegIXL);													break; 
		case 0xAC: XorByte(RegIXH);													break; 
		case 0xAD: XorByte(RegIXL);													break; 
		case 0xB4: OrByte(RegIXH);													break; 
		case 0xB5: OrByte(RegIXL);													break; 
		case 0xBC: CompareByte(RegIXH);												break; 
		case 0xBD: CompareByte(RegIXL);												break; 


		case 0x40: RegB = RegB;														break;
		case 0x41: RegB = RegC;														break;
		case 0x42: RegB = RegD;														break;
		case 0x43: RegB = RegE;														break;
		case 0x47: RegB = RegA;														break;
		case 0x48: RegC = RegB;														break;
		case 0x49: RegC = RegC;														break;
		case 0x4A: RegC = RegD;														break;
		case 0x4B: RegC = RegE;														break;
		case 0x4F: RegC = RegA;														break;

		case 0x50: RegD = RegB;														break;
		case 0x51: RegD = RegC;														break;
		case 0x52: RegD = RegD;														break;
		case 0x53: RegD = RegE;														break;
		case 0x57: RegD = RegA;														break;
		case 0x58: RegE = RegB;														break;
		case 0x59: RegE = RegC;														break;
		case 0x5A: RegE = RegD;														break;
		case 0x5B: RegE = RegE;														break;
		case 0x5F: RegE = RegA;														break;

		case 0x78: RegA = RegB;														break;
		case 0x79: RegA = RegC;														break;
		case 0x7A: RegA = RegD;														break;
		case 0x7B: RegA = RegE;														break;
		case 0x7F: RegA = RegA;														break;



#pragma endregion

		default:
			assert(false && "Unrecognized Opcode");
			break;
	}

	return m_cyclesDD[opcode];
}
